﻿using Nop.Core;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Media;
using Nop.Core.Domain.WindAlert;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Helpers;
using Nop.Services.Localization;
using Nop.Services.Media;
using Nop.Services.Seo;
using Nop.Services.WindAlert;
using Nop.Web.Framework.Controllers;
using Nop.Web.Framework.Kendoui;
using Nop.Web.Framework.Security.Captcha;
using Nop.Web.Models.WindAlert;
using Nop.Web.WindAlert.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;


namespace Nop.Web.Controllers
{
	public partial class WSpotController : BasePublicController
    {
        #region Fields
        private readonly IWorkContext _workContext;
        private readonly IWindDataService _windDataService;
        private readonly IDateTimeHelper _dateTimeHelper;
        private readonly ISpotService _spotService;
        private readonly IPictureService _pictureService;
        private readonly ILocalizationService _localizationService;
        private readonly IForecastDataService _forecastDataService;
        private readonly IWaveDataService _waveDataService;
        private readonly IWindAlertPictureService _windAlertPictureService;

        private readonly MediaSettings _mediaSettings;
        private readonly CaptchaSettings _captchaSettings;
        private readonly CustomerSettings _customerSettings;
        private readonly WindAlertSettings _windAlertSettings;

        private readonly IWindHelpers _windHelper;

        private Random random = new Random();
        #endregion

        #region Constructors

        public WSpotController(IWorkContext workContext, 
            IWindDataService windDataService,
            IDateTimeHelper dateTimeHelper,
            ISpotService SpotService,
            IPictureService pictureService,
            IWindAlertPictureService windAlertPictureService,
            ILocalizationService localizationService,
            MediaSettings mediaSettings,
            CustomerSettings customerSettings,
            CaptchaSettings captchaSettings,
            IForecastDataService forecastDataService,
            IWaveDataService waveDataService,
            IWindHelpers windHelper,
            WindAlertSettings windAlertSettings)
        {
            this._workContext = workContext;
            this._windDataService = windDataService;
            this._dateTimeHelper = dateTimeHelper;
            this._spotService = SpotService;
            this._pictureService = pictureService;
            this._localizationService = localizationService;
            this._mediaSettings = mediaSettings;
            this._customerSettings = customerSettings;
            this._captchaSettings = captchaSettings;
            this._forecastDataService = forecastDataService;
            this._waveDataService = waveDataService;
            this._windHelper = windHelper;
            this._windAlertSettings = windAlertSettings;
            this._windAlertPictureService = windAlertPictureService;
        }

        #endregion

        #region Utilities

        [NonAction]
        protected bool IsCurrentUserRegistered()
        {
            return _workContext.CurrentCustomer.IsRegistered();
        }

        [NonAction]
        protected string GetForecastTimeString()
        {
            int currentHour = DateTime.Now.Hour;
            int[] hours = new int[] { 0, 3, 6, 9, 12, 15, 18, 21 };
            var nearest = hours.OrderBy(x => Math.Abs((long)x - currentHour)).First();
            string results =  "w" + Array.IndexOf(hours, nearest).ToString();
            return results;
        }
        [NonAction]
        protected void PrepareSpotModel(SpotModel model, Spot spot, bool prepareComments)
        {
            if (spot == null)
                throw new ArgumentNullException("spot");

            if (model == null)
                throw new ArgumentNullException("model");

            model.Id = spot.Id;
            model.MetaTitle = spot.MetaTitle;
            model.MetaDescription = spot.MetaDescription;
            model.MetaKeywords = spot.MetaKeywords;
            model.SeName = spot.GetSeName();
            model.Name = spot.Name;
            //cameras
            model.AvailableCameras = spot.Cameras
                 .Select(x =>
                 {
                     var camModel = x.ToModel();
                     return camModel;
                 })
                .ToList();

            double mNumber = 1 + ((double)random.Next(1, 4) / (double)10);

            #region stations
            if (spot.IsStation || spot.StationIsMetar)
            {
                var stationModel = new StationModel();
                stationModel.Spot = spot;
                stationModel.IsForecast = false;
                var windStationData = _windDataService.GetWindDataBySpotIdTop1(spot.Id);
                if (windStationData != null)
                {
                    var windDataRow = new WindDataRowModel()
                    {
                        StationAdjustedTime = windStationData.StationAdjustedTime,
                        WindSpeed = windStationData.WindSpeed,
                        WindGust = windStationData.WindGust,
                        WindDirection = windStationData.WindDirection,
                        WindDirectionLetters = _windHelper.getDirectionNumToLetters(windStationData.WindDirection),
                    };
                    stationModel.WindData = windDataRow;
                }
                model.Station = stationModel;
            }
            else if (spot.IsForecast)
            {
                var forecastSpotData = _forecastDataService.GetForecastDataBySpotIdAndName(spot.Id, GetForecastTimeString());
                if (forecastSpotData != null)
                {
                    var stationModel = new StationModel();
                    stationModel.Spot = spot;
                    stationModel.IsForecast = true;
                    var windDataRow = new WindDataRowModel()
                    {
                        StationAdjustedTime = forecastSpotData.ForecastTime,
                        WindSpeed = (int)forecastSpotData.WindSpeed,
                        WindGust = (int) (forecastSpotData.WindSpeed * mNumber),
                        WindDirection = forecastSpotData.WindDirection,
                        WindDirectionLetters = _windHelper.getDirectionNumToLetters(forecastSpotData.WindDirection),
                    };
                    stationModel.WindData = windDataRow;
                    model.Station = stationModel;
                }
                else
                    model.Station = null;
            }
            else
                model.Station = null;

            #endregion
            
            #region wave
            model.WaveData = null;
            if (spot.IsWave)
            {
                var waveSpotData = _waveDataService.GetWaveDataBySpotIdTop1(spot.Id);
                if (waveSpotData != null)
                {
                    var waveDataRow = new WaveDataRowModel()
                    {
                        SpotId = spot.Id,
                        SpotName = spot.Name,
                        WaveAdjustedTime = waveSpotData.WaveAdjustedTime,
                        Hmax = waveSpotData.Hmax,
                        Hs = waveSpotData.Hs,
                        Direction = waveSpotData.Direction,
                        tav = waveSpotData.Tav,
                        Temperture = waveSpotData.Temperture,
                        DirectionLetters = _windHelper.getDirectionNumToLetters(waveSpotData.Direction),
                        IsForecast = false,
                    };
                    model.WaveData = waveDataRow;
                }
            }
            else if(spot.IsForecast)
            { 
                var forecastSpotData = _forecastDataService.GetForecastDataBySpotIdAndName (spot.Id, GetForecastTimeString());
                if (forecastSpotData != null)
                {
                    if (forecastSpotData.WaveDirection != 0  && forecastSpotData.Height != 0  && forecastSpotData.Period != 0  )
                    {
                        var waveDataRow = new WaveDataRowModel()
                        {
                            SpotId = spot.Id,
                            SpotName = spot.Name,
                            WaveAdjustedTime = forecastSpotData.ForecastTime,
                            Hs =forecastSpotData.Height,
                            Hmax = (forecastSpotData.Height * mNumber),
                            Direction = forecastSpotData.WaveDirection,
                            tav = forecastSpotData.Period,
                            Temperture = 0,
                            DirectionLetters = _windHelper.getDirectionNumToLetters(forecastSpotData.WaveDirection),
                            IsForecast = true,
                        };
                        model.WaveData = waveDataRow;
                    }
                }
            }

            #endregion

            #region Comments
            model.AllowComments = true; //spot.AllowComments;

            //get spot coments for today
            DateTime nowDt = _dateTimeHelper.ConvertToUserTime(DateTime.Now.AddDays(0));
            TimeZoneInfo timeZone = _dateTimeHelper.CurrentTimeZone;
            //today
            DateTime t1 = new DateTime(nowDt.Year, nowDt.Month, nowDt.Day);

            DateTime? startTime1 = _dateTimeHelper.ConvertToUtcTime(t1, timeZone);
            DateTime? endTime1 = _dateTimeHelper.ConvertToUtcTime(t1.AddDays(1), timeZone);

            model.NumberOfComments = spot.SpotComments.Where(pr => pr.CreatedOnUtc >= startTime1 && pr.CreatedOnUtc < endTime1).Count();
           
            #endregion

            if (!IsCurrentUserRegistered())
                model.IsAuthenticated = false;
            else
                model.IsAuthenticated = true;

        }

        [NonAction]
        protected SpotListModel PrepareSpotListModel(bool isMetar)
        {
            IList<Spot> spots = null ;
            var model = new SpotListModel();
            if(isMetar)
                spots = _spotService.GetAllSpots(showOnAviasion:true);
            else
                spots = _spotService.GetAllSpots(showOnHome: true);
            model.Spots = spots
                .Select(x =>
                {
                    var spotModel = new SpotModel();
                    PrepareSpotModel(spotModel, x, false);
                    return spotModel;
                })
                .ToList();

            return model;
        }

        [NonAction]
        private ListCommentModel SpotCommentItems(int spotId = 0)
        {
            var model = new ListCommentModel();
            model.SpotId = spotId;
            //get spot coments for today
            DateTime nowDt = _dateTimeHelper.ConvertToUserTime(DateTime.Now.AddDays(0));
            TimeZoneInfo timeZone = _dateTimeHelper.CurrentTimeZone;
            //today
            DateTime t1 = new DateTime(nowDt.Year, nowDt.Month, nowDt.Day);

            DateTime? startTime1 = _dateTimeHelper.ConvertToUtcTime(t1, timeZone);
            DateTime? endTime1 = _dateTimeHelper.ConvertToUtcTime(t1.AddDays(1), timeZone);
            var allComments = _spotService.GetAllComments(spotId, 0, startTime1, endTime1).OrderByDescending(c => c.Id).ToList();
            model.NumberOfComments = allComments.Count;
            foreach (var sc in allComments)
            {
                var commentModel = new SpotCommentModel()
                {
                    Id = sc.Id,
                    CustomerId = sc.CustomerId,
                    CustomerName = sc.Customer.FormatUserName(),
                    CommentText = sc.CommentText,
                    CreatedOn = _dateTimeHelper.ConvertToUserTime(sc.CreatedOnUtc, DateTimeKind.Utc),
                    IsCustomerAllowedToEditDeleteComment = _spotService.IsCustomerAllowedToEditDeleteComment(_workContext.CurrentCustomer, sc),
                    AllowViewingProfiles = _customerSettings.AllowViewingProfiles && sc.Customer != null && !sc.Customer.IsGuest(),
                    SpotName = sc.Spot.Name,
                };
                if (_customerSettings.AllowCustomersToUploadAvatars)
                {
                    var facebookAutenticationRecored = sc.Customer.ExternalAuthenticationRecords.Where(x => x.ProviderSystemName == "ExternalAuth.Facebook").FirstOrDefault();
                    if (facebookAutenticationRecored != null)
                    {
                        commentModel.CustomerAvatarUrl = "http://graph.facebook.com/" + facebookAutenticationRecored.ExternalIdentifier + "/picture?type=small";
                    }
                    else
                    {
                        commentModel.CustomerAvatarUrl = _pictureService.GetPictureUrl(
                            sc.Customer.GetAttribute<int>(SystemCustomerAttributeNames.AvatarPictureId),
                            50,//_mediaSettings.AvatarPictureSize,
                            _customerSettings.DefaultAvatarEnabled,
                            defaultPictureType: PictureType.Avatar);
                    }
                }
                if (sc.PictureId > 0)
                {
                    commentModel.PictureUrl = _windAlertPictureService.GetPictureUrl(
                        sc.PictureId,
                        50,//_mediaSettings.AvatarPictureSize,
                        false, defaultPictureType: PictureType.Entity);
                    commentModel.PictureUrlFullSize = _windAlertPictureService.GetPictureUrl(sc.PictureId);
                }
                else
                    commentModel.PictureUrl = null;

                model.Comments.Add(commentModel);
            }
            return model;
        }
        #endregion

        #region spots
        [HttpPost]
        [ValidateInput(false)]
        public ActionResult SpotTableAjax(string var1 = "False", string var2 = "") //var1 isMetar
        {
            bool isMetar = Convert.ToBoolean(var1);
            var model = PrepareSpotListModel(isMetar);
            return Json(new
            {
                html = this.RenderPartialViewToString("SpotTable", model),
            });
            //return PartialView(model);
        }

        [ValidateInput(false)]
        public ActionResult SpotTable(bool isMetar= false) //var1 isMetar
        {
            var model = PrepareSpotListModel(isMetar);
            return PartialView(model);
        }

        public ActionResult Spot(int spotId = 1)
        {
            var spot = _spotService.GetSpotById(spotId);
            if (spot == null)
                return RedirectToRoute("HomePage");

            var model = new SpotModel();
            PrepareSpotModel(model, spot, true);

            if (!IsCurrentUserRegistered())
                model.IsAuthenticated = false;
            else
                model.IsAuthenticated = true;

            return View(model);
        }
        #endregion 

        #region Comments

        [ChildActionOnly]
        public ActionResult SpotCommentsListBox(int spotId = 0)
        {
            var model = SpotCommentItems(spotId);
            if (!IsCurrentUserRegistered() && _windAlertSettings.BlockCameras)
                model.IsAuthenticated = false;
            else
                model.IsAuthenticated = true;
            
            return PartialView(model);
        }

       // [ChildActionOnly]
        public ActionResult SpotCommentsList(int spotId = 0)
        {
            var model = SpotCommentItems(spotId);
            return PartialView(model);
        }
        
        public ActionResult SpotComments(int spotId = 0)
        {
            var spots = _spotService.GetAllSpots(showOnHome: true);
            var model = new AddSpotCommentModel();
            model.SpotId = spotId;
            model.PictureMaximumSizeBytes = _windAlertSettings.PictureMaximumSizeBytes;
            //spots
            model.AvailableSpots.Add(new SelectListItem() { Text = _localizationService.GetResource("WindAlert.Spot.Comments.SpotId"), Value = "" });
            foreach (var spot in spots)
                model.AvailableSpots.Add(new SelectListItem() { Text = spot.Name, Value = spot.Id.ToString(), Selected = (spot.Id == spotId) });

            //spots sort
            model.AvailableSpotsSort.Add(new SelectListItem() { Text = _localizationService.GetResource("WindAlert.Spot.Comments.AllSpots"), Value = "0" });
            foreach (var spot in spots)
                model.AvailableSpotsSort.Add(new SelectListItem() { Text = spot.Name, Value = spot.Id.ToString(), Selected = (spot.Id == spotId) });

            return View(model);
        }

        [HttpPost, ActionName("Spot")]
        [FormValueRequired("add-comment")]
        [CaptchaValidator]
        public ActionResult AddSpotComment(int spotId, AddSpotCommentModel model, bool captchaValid, HttpPostedFileBase uploadedFile)
        {
            var spot = _spotService.GetSpotById(spotId);
            var spotModel = new SpotModel();
            if (spot == null || !spot.AllowComments)
                return RedirectToRoute("HomePage");

            if (_workContext.CurrentCustomer.IsGuest() )
            {
                ModelState.AddModelError("", _localizationService.GetResource("Blog.Comments.OnlyRegisteredUsersLeaveComments"));
            }

            //validate CAPTCHA
            if (_captchaSettings.Enabled && _captchaSettings.ShowOnBlogCommentPage && !captchaValid)
            {
                ModelState.AddModelError("", _localizationService.GetResource("Common.WrongCaptcha"));
            }

            if (ModelState.IsValid)
            { 
                int pictureId = 0;
                try
                {
                    var commentPicture = new Picture();
                    if ((uploadedFile != null) && (!String.IsNullOrEmpty(uploadedFile.FileName)))
                    {
                        int pictureMaxSize = _windAlertSettings.PictureMaximumSizeBytes; 
                        if (uploadedFile.ContentLength > pictureMaxSize)
                            throw new NopException(string.Format(_localizationService.GetResource("Windalert.Spot.Comments.MaximumUploadedFileSize"), pictureMaxSize));

                        byte[] commentPictureBinary = uploadedFile.GetPictureBits();
                        commentPicture = _windAlertPictureService.InsertPicture(commentPictureBinary, uploadedFile.ContentType, null, true);
                    }
                    if (commentPicture != null)
                        pictureId = commentPicture.Id;

                }
                catch (Exception exc)
                {
                    ModelState.AddModelError("", exc.Message);
                    PrepareSpotModel( spotModel, spot, true);
                    return View(spotModel);
                }
                var comment = new SpotComment()
                {
                    SpotId = spot.Id,
                    CustomerId = _workContext.CurrentCustomer.Id,
                    CommentText = model.CommentText,
                    PictureId = pictureId,
                    CreatedOnUtc = DateTime.UtcNow,
                };
                spot.SpotComments.Add(comment);
                //update totals
                spot.CommentCount = spot.SpotComments.Count;
                _spotService.UpdateSpot(spot);

                //activity log
                //_customerActivityService.InsertActivity("PublicStore.AddBlogComment", _localizationService.GetResource("ActivityLog.PublicStore.AddBlogComment"));

                //The text boxes should be cleared after a comment has been posted
                //That' why we reload the page
                TempData["windalert.spot.addcomment.result"] = _localizationService.GetResource("Windalert.Spot.Comments.SuccessfullyAdded");
                return RedirectToRoute("Spot", new { SeName = spot.GetSeName() });
            }

            //If we got this far, something failed, redisplay form
            PrepareSpotModel(spotModel, spot, true);
            if (!IsCurrentUserRegistered() && _windAlertSettings.BlockCameras)
                spotModel.IsAuthenticated  = false;
            else
                spotModel.IsAuthenticated = true;
            return View(spotModel);
        }

        [ChildActionOnly]
        public ActionResult SpotCommentsAdd(int spotId = 0)
        {
            var spots = _spotService.GetAllSpots(showOnHome: true);
            var model = new AddSpotCommentModel();
            model.SpotId = spotId;
            model.PictureMaximumSizeBytes = _windAlertSettings.PictureMaximumSizeBytes;
            //spots
            model.AvailableSpots.Add(new SelectListItem() { Text = _localizationService.GetResource("WindAlert.Spot.Comments.SpotId"), Value = "" });
            foreach (var spot in spots)
                model.AvailableSpots.Add(new SelectListItem() { Text = spot.Name, Value = spot.Id.ToString(), Selected = (spot.Id == spotId) });

            return PartialView(model);
        }

        [ChildActionOnly]
        public ActionResult SpotCommentsAddPopup(int spotId = 0)
        {
            var spots = _spotService.GetAllSpots(showOnHome: true);
            var model = new AddSpotCommentModel();
            model.SpotId = spotId;
            model.PictureMaximumSizeBytes = _windAlertSettings.PictureMaximumSizeBytes;
            //spots
            model.AvailableSpots.Add(new SelectListItem() { Text = _localizationService.GetResource("WindAlert.Spot.Comments.SpotId"), Value = "" });
            foreach (var spot in spots)
                model.AvailableSpots.Add(new SelectListItem() { Text = spot.Name, Value = spot.Id.ToString(), Selected = (spot.Id == spotId) });

            return PartialView(model);
        }

        public ActionResult CommentDelete(int id, int spotId)
        {
            var spotComment = _spotService.GetSpotCommentById (id);
            if (spotComment != null)
            {
                if (!_spotService.IsCustomerAllowedToEditDeleteComment(_workContext.CurrentCustomer, spotComment))
                {
                    return new HttpUnauthorizedResult();
                }
                _spotService.DeleteSpotComment(spotComment);
            }
            TempData["windalert.spot.addcomment.result"] = _localizationService.GetResource("Windalert.Spot.Comments.SuccessfullyDeleted");
            if (spotId == 0)
                return RedirectToAction("SpotComments");
            else
            {
                var slug = _spotService.GetSpotById(spotId).GetSeName();
                return RedirectToRoute("Spot", new { SeName = slug });
            }
        }

        #endregion

        #region Chart
        public ActionResult WaveChart(int spotId = 1)
        {
            ViewBag.SpotName = _spotService.GetSpotById(spotId).Name;
            ViewBag.SpotId = spotId;
            return View();
        }
        public ActionResult WaveChartJson(int spotId = 1,int backHours = 2)
        {
            if (backHours > 24)
                backHours = 2;
            var spot = _spotService.GetSpotById(spotId);

            //int maxSpeedGust = 10;
            int numberOfColumns = backHours * 12;

            var waveSpotData = _waveDataService.GetWaveDataBySpotId(spotId, (backHours * (-1)));
            var startDate = DateTime.Now.AddHours(backHours * (-1));

            var chartRows = new List<ChartRowModel>();

            for (int i = 0; i < numberOfColumns; i++)
            {
                var curDate = startDate.AddMinutes(i * ((60 * backHours) / numberOfColumns));
                var enddate = startDate.AddMinutes((i + 1) * ((60 * backHours) / numberOfColumns));
                var waveDataRow = new ChartRowModel();
                if (backHours < 24)
                    waveDataRow.Time = startDate.AddMinutes(i * 5).ToShortTimeString();
                else
                    waveDataRow.Time = startDate.AddMinutes(i * 5).ToString("dd/MM/yy HH:mm");

                foreach (var waveSpotDataRow in waveSpotData)
                {
                    if (waveSpotDataRow.WaveAdjustedTime > curDate && waveSpotDataRow.WaveAdjustedTime < enddate)
                    {
                        waveDataRow.Hs = waveSpotDataRow.Hs;
                        waveDataRow.Hmax = waveSpotDataRow.Hmax;
                    }
                }

                chartRows.Add(waveDataRow);
            }

            IEnumerable Data = chartRows;

            return Json(Data, JsonRequestBehavior.AllowGet);

        }

        public ActionResult WindChart(int spotId = 1)
        {
            ViewBag.SpotName = _spotService.GetSpotById(spotId).Name;
            ViewBag.SpotId = spotId;
            return View();
        }
        public ActionResult WindChartJson(int spotId = 1, int backHours = 2)
        {
            if (backHours > 24)
                backHours = 2;
            var spot = _spotService.GetSpotById(spotId);

            //int maxSpeedGust = 10;
            int numberOfColumns = backHours * 12;


            var windStationData = _windDataService.GetWindDataBySpotId(spotId, (backHours * (-1)));
            var startDate = DateTime.Now.AddHours(backHours * (-1));

            var chartRows = new List<ChartRowModel>();
            
            for (int i = 0; i < numberOfColumns; i++)
            {
                var curDate = startDate.AddMinutes(i * ((60 * backHours) / numberOfColumns));
                var enddate = startDate.AddMinutes((i + 1) * ((60 * backHours) / numberOfColumns));
                var windDataRow = new ChartRowModel();
                if (backHours < 24)
                    windDataRow.Time = startDate.AddMinutes(i * 5).ToShortTimeString();
                else
                    windDataRow.Time = startDate.AddMinutes(i * 5).ToString("dd/MM/yy HH:mm");

                foreach (var windStationDataRow in windStationData)
                {
                    if (windStationDataRow.StationAdjustedTime > curDate && windStationDataRow.StationAdjustedTime < enddate)
                    {
                        windDataRow.Speed = windStationDataRow.WindSpeed;
                        windDataRow.Gust = windStationDataRow.WindGust;
                    }
                }

                chartRows.Add(windDataRow);
            }

            IEnumerable Data  = chartRows;
         
            return Json(Data, JsonRequestBehavior.AllowGet);
        }

        #endregion
    }

}
